/*
 Copyright (c) 2013 MeherTJ G. All rights reserved.
 License: LGPL for personnal study or free software.
 */

#include "zen_image.h"
#include <cmath>

namespace Zen {
	class ImageFilter
	{
	public:
		virtual bool operator()(std::shared_ptr<Image> target) = 0;
	};

	class ImageFilterRainbow : public ImageFilter
	{
	public:
		ImageFilterRainbow(float angle, float move);
		virtual bool operator()(std::shared_ptr<Image> target) override;
		static Color4f GetRainbowColor(float v);
		static float XYTrasform(float x, float y, float xv, float yv);
	protected:
		float m_move{};
		float m_x{}, m_y{};
	};

	inline float ImageFilterRainbow::XYTrasform(float x, float y, float xv, float yv)
	{
		if (x < 0) xv -= 1;
		if (y < 0) yv -= 1;
		return x * xv + y * yv;
	}

	/// <param name="v">0. ~ 1.</param>
	inline Color4f ImageFilterRainbow::GetRainbowColor(float v) {
		const Color4f c[8] = {
			Color4b(0xFF0000FF),
			Color4b(0xFF7F00FF),
			Color4b(0xFFFF00FF),
			Color4b(0x00FF00FF),
			Color4b(0x00FFFFFF),
			Color4b(0x0000FFFF),
			Color4b(0xFF00FFFF),
			Color4b(0xFF0000FF),
		};
		v = (v - floor(v)) * 7;
		int n = (int)v;
		v = v - n;
		return ColorLerp(c[n], c[n + 1], v);
	}

	inline ImageFilterRainbow::ImageFilterRainbow(float angle, float move)
	{
		m_move = move;
		m_x = cosf(angle);
		m_y = sinf(angle);
		auto s = abs(m_x) + abs(m_y);
		m_x /= s;
		m_y /= s;
	}

	inline bool ImageFilterRainbow::operator()(std::shared_ptr<Image> image) {
		if (!image || image->PixelType() == EPixel::None || image->Width() == 0 || image->Height() == 0) return false;

		std::vector<float> values(image->Width());
		for (size_t i = 0; i < image->Width(); ++i) {
			values[i] = (float)i / (float)image->Width();
		}
		std::function<void(Color4f const& color, size_t x, size_t y)> setColor;

		if (image->PixelType() == EPixel::Grey)
		{
			setColor = [&image](Color4f const& color, size_t x, size_t y) {
				*(image->Data() + y * image->Width() + x) = uint8_t(color.green * 255);
			};
		}
		else if (image->PixelType() == EPixel::GA)
		{
			setColor = [&image](Color4f const& color, size_t x, size_t y) {
				auto p = (image->Data() + (y * image->Width() + x) * 2);
				p[0] = uint8_t(color.green * 255);
				p[1] = 0xff;
			};
		}
		else if (image->PixelType() == EPixel::RGB)
		{
			setColor = [&image](Color4f const& color, size_t x, size_t y) {
				auto p = (image->Data() + (y * image->Width() + x) * 3);
				p[0] = uint8_t(color.red * 255);
				p[1] = uint8_t(color.green * 255);
				p[2] = uint8_t(color.blue * 255);
			};
		}
		else if (image->PixelType() == EPixel::RGBA)
		{
			setColor = [&image](Color4f const& color, size_t x, size_t y) {
				auto p = (image->Data() + (y * image->Width() + x) * 4);
				p[0] = uint8_t(color.red * 255);
				p[1] = uint8_t(color.green * 255);
				p[2] = uint8_t(color.blue * 255);
				p[3] = 0xff;
			};
		}

		for (size_t j = 0; j < image->Height(); ++j)
		{
			float yv = (float)j / (float)image->Height();
			for (size_t i = 0; i < image->Width(); ++i)
			{
				float xv = values[i];
				auto c = GetRainbowColor(XYTrasform(m_x, m_y, xv, yv) + m_move);
				setColor(c, i, j);
			}
		}
		return true;
	}
}
